package org.hamster.common;

import java.math.BigInteger;
import java.util.stream.Stream;

/**
 * Utility class for validating IP addresses.
 * Provides methods to check if an IP address is a valid IPv4 or IPv6 address.
 */
public class IPValidationUtil {
    
    /**
     * Checks if the given IP string is a valid IPv4 address without using regular expressions.
     *
     * @param ip the IP address string to validate
     * @return true if the IP is valid, false otherwise
     */
    public static boolean isValidIPv4(String ip) {
        if (ip == null || ip.isEmpty()) {
            return false;
        }

        // Split by dot
        String[] parts = ip.split("\\.");
        
        // Must have exactly 4 parts
        if (parts.length != 4) {
            return false;
        }
        
        // Check each part
        for (String part : parts) {
            // Each part must not be empty
            if (part.isEmpty()) {
                return false;
            }
            
            // Each part must not have leading zeros (except for "0" itself)
            if (part.length() > 1 && part.charAt(0) == '0') {
                return false;
            }
            
            // Each part must be a valid integer
            try {
                int num = Integer.parseInt(part);
                
                // Each part must be in range 0-255
                if (num < 0 || num > 255) {
                    return false;
                }
            } catch (NumberFormatException e) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Checks if the given IP string is a valid IPv6 address without using regular expressions.
     * 
     * @param ipv6 the IPv6 address string to validate
     * @return true if the IPv6 is valid, false otherwise
     */
    public static boolean isValidIPv6(String ipv6) {
        if (ipv6 == null || ipv6.isEmpty()) {
            return false;
        }
        
        // Handle special case of just "::"
        if ("::".equals(ipv6)) {
            return true;
        }
        
        // Handle IPv6 addresses with compressed format (::)
        String[] parts = ipv6.split("::", -1);
        if (parts.length > 2) {
            // More than one "::" is invalid
            return false;
        }
        
        // Count total segments
        int totalSegments = 0;
        boolean hasDoubleColon = ipv6.contains("::");
        
        if (hasDoubleColon) {
            // Count segments before and after "::"
            if (!parts[0].isEmpty()) {
                totalSegments += parts[0].split(":").length;
            }
            if (!parts[1].isEmpty()) {
                totalSegments += parts[1].split(":").length;
            }
        } else {
            // No compression, count all segments
            totalSegments = ipv6.split(":").length;
        }
        
        // IPv6 must have between 1 and 8 segments
        if (totalSegments < 1 || totalSegments > 8) {
            return false;
        }
        
        // If no compression and not exactly 8 segments, it's invalid
        if (!hasDoubleColon && totalSegments != 8) {
            return false;
        }
        
        // Validate each segment
        String[] segments = ipv6.split(":");
        for (String segment : segments) {
            // Empty segments are only valid as part of "::"
            if (segment.isEmpty()) {
                continue;
            }
            
            // Each segment must be a valid hexadecimal number with 1-4 digits
            if (segment.length() > 4) {
                return false;
            }
            
            // Check that all characters are valid hexadecimal digits
            for (int i = 0; i < segment.length(); i++) {
                char c = segment.charAt(i);
                if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
                    return false;
                }
            }
            
            // Check that the value is within valid range for IPv6 segment (0-0xFFFF)
            try {
                BigInteger value = new BigInteger(segment, 16);
                if (value.compareTo(BigInteger.valueOf(0xFFFF)) > 0) {
                    return false;
                }
            } catch (NumberFormatException e) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Determines if an IP string represents an IPv6 address.
     * 
     * @param ip the IP string to check
     * @return true if the IP represents an IPv6 address, false otherwise
     */
    public static boolean isIPv6Address(String ip) {
        // IPv6 addresses contain colons, while IPv4 addresses contain dots
        return ip != null && ip.contains(":");
    }
    
    /**
     * Determines if an IP string represents an IPv4 address.
     * 
     * @param ip the IP string to check
     * @return true if the IP represents an IPv4 address, false otherwise
     */
    public static boolean isIPv4Address(String ip) {
        // IPv4 addresses contain dots, while IPv6 addresses contain colons
        return ip != null && ip.contains(".") && !ip.contains(":");
    }
}